﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;

namespace K9Nano.Domain
{
    public interface IRepository<TEntity, TKey> where TEntity : class, IEntity<TKey>
        where TKey : IEquatable<TKey>
    {
        #region Select/Get/Query

        IQueryable<TEntity> Query();

        IQueryable<TEntity> QueryAsNoTracking();

        TEntity Get(TKey id);

        ValueTask<TEntity> GetAsync(TKey id, CancellationToken cancellation = default);

        IQueryable<TEntity> GetAllIncluding(params Expression<Func<TEntity, object>>[] propertySelectors);

        List<TEntity> GetAllList();

        ValueTask<List<TEntity>> GetAllListAsync(CancellationToken cancellation = default);

        List<TEntity> GetAllList([NotNull]Expression<Func<TEntity, bool>> predicate);

        ValueTask<List<TEntity>> GetAllListAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellation = default);

        ValueTask<List<TEntity>> GetPagedListAsync(Expression<Func<TEntity, bool>> predicate, [NotNull]Func<IQueryable<TEntity>, IQueryable<TEntity>> orderByCreator, int pageIndex, int pageSize, CancellationToken cancellation = default);

        ValueTask<List<TEntity>> GetPagedListAsync(Expression<Func<TEntity, bool>> predicate, [NotNull]Expression<Func<TEntity, object>> orderBy, int pageIndex, int pageSize, CancellationToken cancellation = default);

        ValueTask<List<TEntity>> GetPagedListAsync(Expression<Func<TEntity, object>> orderBy, int pageIndex, int pageSize, CancellationToken cancellation = default);

        TEntity Single([NotNull]Expression<Func<TEntity, bool>> predicate);

        ValueTask<TEntity> SingleAsync([NotNull]Expression<Func<TEntity, bool>> predicate, CancellationToken cancellation = default);

        TEntity FirstOrDefault(TKey id);

        ValueTask<TEntity> FirstOrDefaultAsync(TKey id, CancellationToken cancellation = default);

        TEntity FirstOrDefault([NotNull]Expression<Func<TEntity, bool>> predicate);

        ValueTask<TEntity> FirstOrDefaultAsync([NotNull]Expression<Func<TEntity, bool>> predicate, CancellationToken cancellation = default);

        TEntity FirstOrDefault([NotNull] Expression<Func<TEntity, bool>> predicate, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> configOrderBy);

        ValueTask<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> configOrderBy, CancellationToken cancellation = default);

        bool Any();

        bool Any([NotNull] Expression<Func<TEntity, bool>> predicate);

        ValueTask<bool> AnyAsync(CancellationToken cancellation);

        ValueTask<bool> AnyAsync([NotNull] Expression<Func<TEntity, bool>> predicate, CancellationToken cancellation);

        #endregion

        #region Insert

        [NotNull] TEntity Insert([NotNull]TEntity entity);

        ValueTask<TEntity> InsertAsync([NotNull]TEntity entity, CancellationToken cancellation = default);

        TKey InsertAndGetId([NotNull]TEntity entity);

        ValueTask<TKey> InsertAndGetIdAsync([NotNull]TEntity entity, CancellationToken cancellation = default);

        TEntity InsertOrUpdate([NotNull]TEntity entity);

        ValueTask<TEntity> InsertOrUpdateAsync([NotNull]TEntity entity, CancellationToken cancellation = default);

        TKey InsertOrUpdateAndGetId([NotNull]TEntity entity);

        ValueTask<TKey> InsertOrUpdateAndGetIdAsync([NotNull]TEntity entity, CancellationToken cancellation = default);

        int AddRange([NotNull]IEnumerable<TEntity> entities);

        ValueTask<int> AddRangeAsync([NotNull]IEnumerable<TEntity> entities, CancellationToken cancellation = default);

        #endregion

        #region Update

        [NotNull] TEntity Update([NotNull]TEntity entity);

        ValueTask<TEntity> UpdateAsync([NotNull]TEntity entity, CancellationToken cancellation = default);

        ValueTask UpdateAsync([NotNull]Expression<Func<TEntity, bool>> predicate, [NotNull]TEntity newEntity, CancellationToken cancellation = default);

        #endregion

        #region Delete

        void Delete([NotNull]TEntity entity, bool soft = true);

        ValueTask DeleteAsync([NotNull]TEntity entity, bool soft = true, CancellationToken cancellation = default);

        TEntity Delete(TKey id, bool soft = true);

        ValueTask<TEntity> DeleteAsync(TKey id, bool soft = true, CancellationToken cancellation = default);

        ValueTask DeleteAsync([NotNull]Expression<Func<TEntity, bool>> predicate, bool soft = true, int batchSize = 200, CancellationToken cancellation = default);

        #endregion

        #region Aggregates

        int Count();

        ValueTask<int> CountAsync(CancellationToken cancellation = default);

        int Count([NotNull]Expression<Func<TEntity, bool>> predicate);

        ValueTask<int> CountAsync([NotNull]Expression<Func<TEntity, bool>> predicate, CancellationToken cancellation = default);

        long LongCount();

        ValueTask<long> LongCountAsync(CancellationToken cancellation = default);

        long LongCount([NotNull]Expression<Func<TEntity, bool>> predicate);

        ValueTask<long> LongCountAsync([NotNull]Expression<Func<TEntity, bool>> predicate, CancellationToken cancellation = default);
        
        int Sum(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, int>> selector);
        long Sum(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, long>> selector);
        float Sum(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, float>> selector);
        double Sum(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, double>> selector);
        decimal Sum(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, decimal>> selector);
        ValueTask<int> SumAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, int>> selector, CancellationToken cancellation);
        ValueTask<long> SumAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, long>> selector, CancellationToken cancellation);
        ValueTask<float> SumAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, float>> selector, CancellationToken cancellation);
        ValueTask<double> SumAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, double>> selector, CancellationToken cancellation);
        ValueTask<decimal> SumAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, decimal>> selector, CancellationToken cancellation);

        int Min(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, int>> selector);
        long Min(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, long>> selector);
        float Min(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, float>> selector);
        double Min(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, double>> selector);
        decimal Min(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, decimal>> selector);
        ValueTask<int> MinAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, int>> selector, CancellationToken cancellation);
        ValueTask<long> MinAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, long>> selector, CancellationToken cancellation);
        ValueTask<float> MinAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, float>> selector, CancellationToken cancellation);
        ValueTask<double> MinAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, double>> selector, CancellationToken cancellation);
        ValueTask<decimal> MinAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, decimal>> selector, CancellationToken cancellation);

        int Max(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, int>> selector);
        long Max(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, long>> selector);
        float Max(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, float>> selector);
        double Max(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, double>> selector);
        decimal Max(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, decimal>> selector);
        ValueTask<int> MaxAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, int>> selector, CancellationToken cancellation);
        ValueTask<long> MaxAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, long>> selector, CancellationToken cancellation);
        ValueTask<float> MaxAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, float>> selector, CancellationToken cancellation);
        ValueTask<double> MaxAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, double>> selector, CancellationToken cancellation);
        ValueTask<decimal> MaxAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, decimal>> selector, CancellationToken cancellation);

        double Average(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, int>> selector);
        double Average(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, long>> selector);
        float Average(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, float>> selector);
        double Average(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, double>> selector);
        decimal Average(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, decimal>> selector);
        ValueTask<double> AverageAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, int>> selector, CancellationToken cancellation);
        ValueTask<double> AverageAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, long>> selector, CancellationToken cancellation);
        ValueTask<float> AverageAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, float>> selector, CancellationToken cancellation);
        ValueTask<double> AverageAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, double>> selector, CancellationToken cancellation);
        ValueTask<decimal> AverageAsync(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, decimal>> selector, CancellationToken cancellation);

        #endregion
    }
}